UCOS你问我答系列之系统时钟节拍详解

您所在的位置:网站首页 stm32l5 ucoS UCOS你问我答系列之系统时钟节拍详解

UCOS你问我答系列之系统时钟节拍详解

2024-07-03 01:34| 来源: 网络整理| 查看: 265

前言

系统时钟节拍是多任务得以正常运行的基石,UCOS的系统时钟节拍一般依赖于MCU的硬件定时器.硬件定时器产生固定时间间隔的中断,中断中调用UCOS的系统函数,完成多任务操作系统的基本调度功能.

本文使用的UCOS版本:V2.91.

代码分析讲解

在本例中我们设定uCOS的时钟频率为100HZ,即10ms进入一次系统时钟中断.               

#define OS_TICKS_PER_SEC 100u 每10ms执行的系统时钟中断相关函数如下:

void SysTick_Handler(void) { // 主要作用是累加OSIntNesting值, 标识当前的中断桥套层数. // OSIntNesting > 0时表示当前处于中断处理函数中. OSIntEnter(); // 主要作用是累加OSTime的值,并遍历任务链表,对于OSTCBDly>0的任务,对其OSTCBDly进行减1操作,并将就绪任务的优先级放入任务就绪表. OSTimeTick(); // OSIntNesting进行减一操作, 并找出优先级最高的任务,进行一次任务调度. OSIntExit(); }

下面对相关函数的具体内容进行进一步讲解,先来分析OSTimeTick函数,主要思路流程在代码内以注释的形式体现.

// 对于此函数的分析仅保留了相关的核心代码 void OSTimeTick (void) { #if OS_TIME_GET_SET_EN > 0u OS_ENTER_CRITICAL(); /* Update the 32-bit tick counter */ // 累加OSTime值, 此值可用于标识系统运行时间. OSTime++; OS_EXIT_CRITICAL(); #endif // 确保ucos已经开始调度后才执行下面的代码. if (OSRunning == OS_TRUE) { ptcb = OSTCBList; /* Point at first TCB in TCB list */ /* 遍历任务链表,对于OSTCBDly不为0的任务进行减一操作,如果减一后为0,判断任务是否还在因等待某个事件而挂起, 如果是,则将任务的OSTCBStatPend设置为OS_STAT_PEND_TO.否则设置为OS_STAT_PEND_OK. 判断任务是否执行了任何挂起操作,如果没有,将此优先级的任务放入就绪表. */ while (ptcb->OSTCBPrio != OS_TASK_IDLE_PRIO) { /* Go through all TCBs in TCB list */ OS_ENTER_CRITICAL(); if (ptcb->OSTCBDly != 0u) { /* No, Delayed or waiting for event with TO */ ptcb->OSTCBDly--; /* Decrement nbr of ticks to end of delay */ if (ptcb->OSTCBDly == 0u) { /* Check for timeout */ if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) { ptcb->OSTCBStat &= (INT8U)~(INT8U)OS_STAT_PEND_ANY; /* Yes, Clear status flag */ ptcb->OSTCBStatPend = OS_STAT_PEND_TO; /* Indicate PEND timeout */ } else { ptcb->OSTCBStatPend = OS_STAT_PEND_OK; } if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) { /* Is task suspended? */ OSRdyGrp |= ptcb->OSTCBBitY; /* No, Make ready */ OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; } } } ptcb = ptcb->OSTCBNext; /* Point at next TCB in TCB list */ OS_EXIT_CRITICAL(); } } }

OSTimeTick最最核心的作用就是,递减每个任务的OSTCBDly值,并将已就绪任务的优先级,放入就绪表.  OSTimeTick函数本身并不会触发任务调度,那我们的任务调度在哪里触发呢?且看下面的OSIntExit()函数的分析.

void OSIntExit (void) { #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; #endif /* OSIntExi函数一般在中断中配合OSIntEnter成对出现, 其主要作用是执行完中断处理函数且无嵌套中断时, 判断一下是否有更高优先级的任务就绪了,如果有,执行任务调度.转去运行更高优先级的任务.如果没有,继续回到 之前被中断打断的那个任务去运行. */ if (OSRunning == OS_TRUE) { OS_ENTER_CRITICAL(); if (OSIntNesting > 0u) { /* Prevent OSIntNesting from wrapping */ OSIntNesting--; } if (OSIntNesting == 0u) { /* Reschedule only if all ISRs complete ... */ if (OSLockNesting == 0u) { /* ... and not locked. */ OS_SchedNew(); OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */ #if OS_TASK_PROFILE_EN > 0u OSTCBHighRdy->OSTCBCtxSwCtr++; /* Inc. # of context switches to this task */ #endif OSCtxSwCtr++; /* Keep track of the number of ctx switches */ OSIntCtxSw(); /* Perform interrupt level ctx switch */ } } } OS_EXIT_CRITICAL(); } } 总结

至此我们对于CPU产生系统时钟中断时所做的事情就比较清晰了. 总结一下就是: 遍历任务,递减其延时值.找到延时值降至0的任务将其优先级放入就绪表.在退出中断时判断是否需要进行任务 调度,并根据需要进行任务调度.

问答环节

1.问: 假设一共创建了两个任务,某一时刻都处于delay状态,这时退出系统时钟中断时如何调度. 答: UCOS默认创建了了一个最低优先级的系统任务OS_TaskIdle,这个任务永远处于就绪状态. 在没有其他任务就绪时,会调度OS_TaskIdle这个任务.

2.问: 假定有两个任务,A任务优先级为1,B任务优先级为2.简述整个软件大致运行流程:

void taskA(void *pdata) { while(1) { Led_Flip(); OSTimeDly(100); } } void taskB(void *pdata) { while(1) { serial_print(); OSTimeDly(500); } }

答: 任务创建完成,调用OSStart后,最高优先级的任务A首先运行,LED状态翻转.然后调用OSTimeDly函数, 设置任务A的OSTCBDly值为100, 并且将任务A的优先级从就绪表移除,触发一次任务调度.此时任务B是就绪表 中优先级最高的任务.CPU调度到任务B开始运行B任务.执行serial_print()函数.然后调用OSTimeDly(500); 设置任务B的OSTCBDly值为500, 并且将任务B的优先级从就绪表移除,触发一次任务调度.此时任务A、B都不处于 就绪状态,CPU调度到最低优先级的任务OS_TaskIdle运行.在上述的任何过程中,都可能会触发系统时钟中断, 触发系统时钟中断时,OSTimeTick函数会判断任务A 任务B是否已进入任务延时,对于已经进入任务延时的任务, 递减其OSTCBDly值.如果有任务OSTCBDly减至0,就将其任务优先级放入就绪表.OSIntExit函数找到就绪表中优先级 最高的任务,进行调度.



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3